/***************************************************************************
 *   Copyright (C) 2009 by maarten,,,   *
 *   maarten@maarten-laptop   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "cgpanel/cgpanel.h"
#include "scenemanager/scene/scene.h"
#include <QWidget>
#include <QPainter>
#include <QFileDialog>
#include <QColor>
#include <QMessageBox>
#include <QStyle>
#include <QBuffer>
#include "scenemanager/camera/camera.h"
#include "scenemanager/light/pointlight.h"
#include "objects/objectlist.h"
#include "objects/object.h"
#include "objects/plane.h"
#include "objects/objecttexture.h"
#include "objects/vertex.h"
#include "scenemanager/scene/subscene.h"
#include "scenemanager/scene/shape.h"
#include "scenemanager/material/material.h"
#include <QTimer>

#include <limits>

#include "cgpanel/floatmatrix.h"

#include <algorithm>
#include <math.h>

#include "cgpanel/rasterizer/rasterizer.h"

#include "cgpanel/raytracer/raytracer.h"
#include "cgpanel/raytracer/ray.h"

#include "cgpanel/timer.h"

#include <iostream>
using namespace std;

namespace draw {

CgPanel::CgPanel(QWidget *parent)
 : QWidget(parent)
{
    m_width = 512;
    m_height = 512;
    setFixedSize(m_width, m_height);
    m_frontColor = QColor(0,0,0);
    m_bgColor = QColor(0,0,0); 
    m_pixMap = QPixmap(m_width,m_height);
    m_pixMap.fill(m_bgColor);
    m_sceneMgr = NULL;
    xaverage = 0;
}

void CgPanel::readSceneFile()
{
    if(m_sceneMgr!= NULL)
      delete m_sceneMgr;
    
    QString fileName = QFileDialog::getOpenFileName(this,
      tr("Open object"), "/home/maarten/Documents/CG/project2/assets/xml", 
              tr("Sdl files(*.sdl)")); 
    m_sceneMgr = new scene::SceneManager();
    
    Timer newTimer;
    newTimer.start();
    m_sceneMgr->loadScene(fileName);
    int time = newTimer.elapsed();
    cout << "Load scene time (ms): " << time << endl;
    
    //check if parsing object was succes
    if(m_sceneMgr->getStatus() == -1)
    {
      QMessageBox mb(QMessageBox::Warning, "Scene failed!",
        "Please check the folder locations, scene xml and object files for \
existence.\n");
        mb.exec();
        m_sceneMgr->clear();
        delete m_sceneMgr;
        m_sceneMgr= NULL;
        return;
    }
    
    //m_sceneMgr->printAllElements();  
    m_bgColor = m_sceneMgr->getScene()->bgColor();
    
    // set camera and compute viewplane
    m_camera = m_sceneMgr->getScene()->cam();
    m_camera->computeViewPlane(m_width, m_height);
   
    QMessageBox mb( "Choose renderstyle",
        "Please choose a rendering style.\n",
        QMessageBox::Information,
        QMessageBox::Yes | QMessageBox::Default,
        QMessageBox::No, QMessageBox::Cancel );
    mb.setButtonText( QMessageBox::Yes, "Raytracing" );
    mb.setButtonText( QMessageBox::No, "Rasterization" );
    mb.setButtonText( QMessageBox::Cancel, "Cancel" );
    
    int modus = -1;
    switch( mb.exec() ) {
      case QMessageBox::Yes:
          modus = 1;
          break;
      case QMessageBox::No:
          modus = 2;  
          break;
    }
    
    if( modus == 1 ){
      // RAYTRACING
      m_rayTracer = new RayTracer(m_width, m_height,
m_sceneMgr->getScene()->antiAliasing());
      m_rayTracer->initViewRays(m_camera);
      startRaytracing();
    }else if( modus == 2 ){
      // RASTERIZATION
      m_rasterizer = new Rasterizer(m_width, m_height, m_sceneMgr);
      startRasterizing();
    }
}

void CgPanel::cleanScreen(){
    if(m_sceneMgr ==NULL){
      QMessageBox msgBox;
      msgBox.setText("No scene loaded...");
      msgBox.exec();
      return;
    }
    
    m_planesToDraw.clear();
    m_sceneMgr->clear();
    m_pixMap.fill(m_bgColor);
    update();
}

void CgPanel::paintEvent( QPaintEvent * )
{
    QPainter p( this );

    // draw pixmap
    p.drawPixmap(0,0,m_width,m_height,m_pixMap);
}

void CgPanel::startRaytracing()
{  
    Timer newTimer;
    
    // raytracer: compute intersections
    QPainter p(&m_pixMap);
    m_rayTracer->setBgcolor(m_bgColor.redF(), m_bgColor.greenF(),
                            m_bgColor.blueF());
    //p.setPen(QPen(Qt::yellow, 1));
    cout << "Start preparing..." << endl;
    newTimer.start();
    
    // fill objects to draw vector
    prepareScene();
    // set ref to cgpanel
    m_rayTracer->setShapes(m_planesToDraw,m_sceneMgr );
    int time = newTimer.elapsed();
    cout << "Acceleration time(ms): " << time << endl;
    
    QMessageBox::about(this,"All loaded", "Click to start raytracing...\n");
    
    newTimer.restart();
    int intervalTime = 0;
    int antialiasing = m_rayTracer->getAntialiasing();
    float totalRays = antialiasing*antialiasing;
    
    if(antialiasing != 1){

    for(int xPix = 0; xPix < m_width; xPix+=1){

      for(int yPix = 0; yPix < m_height; yPix+=1){  
      float red = 0;
      float green = 0;
      float blue = 0;

      for(int xp = 0; xp < antialiasing; ++xp){
        for(int yp = 0; yp < antialiasing; ++yp){
          m_rayTracer->initViewSubFase(xPix,yPix,xp,yp);
          m_rayTracer->rayColor(m_rayTracer->getRay(xPix*antialiasing + xp,
yPix*antialiasing +yp));
          red+=m_rayTracer->getPenColor().redF();
          green+=m_rayTracer->getPenColor().greenF();
          blue+=m_rayTracer->getPenColor().blueF();
          
        }
      }
      red/=totalRays;
      green/=totalRays;
      blue/=totalRays;
      m_frontColor.setRgbF(red, green, blue);
      p.setPen(m_frontColor);
      p.drawPoint(yPix, xPix); 
        
      }
    }
    } else {
      for(int xPix = 0; xPix < m_width*antialiasing; xPix+=antialiasing){
      
        float red = 0;
        float green = 0;
        float blue = 0;
        for(int yPix = 0; yPix < m_height*antialiasing; yPix+=antialiasing){  
          m_rayTracer->rayColor(m_rayTracer->getRay(xPix, yPix));
          m_frontColor = m_rayTracer->getPenColor();
          p.setPen(m_frontColor);
          p.drawPoint(yPix, xPix); 
        }
      
      }
    }
    time = newTimer.elapsed();
    cout << "Raytracing time(ms): " << time << endl;
    update();
}

void CgPanel::startRasterizing()
{
    // raytracer: compute intersections
    QPainter p(&m_pixMap);
    p.setPen(QPen(Qt::yellow, 1));
    
    // prepare shapes
    prepareScene();
    
    cout << "Setting boundaries\n";
    // set boundaries
    m_rasterizer->setBoundaries(&m_planesToDraw);
    
    cout << "Setting canonical\n";
    // calculate canonical viewvolume
    m_rasterizer->calculateCanonicalBox();
    
    cout << "Setting view\n";
    // calculate viewTransformation
    m_rasterizer->calculateViewMatrix();
    
    cout << "Setting perspective\n";
    // calculate perspective
    m_rasterizer->calculatePerspectiveMatrix(); // will also calculate final
    
    cout << "Starting to draw\n";

    for(int pl = 0; pl < m_planesToDraw.size();++pl){    
      m_rasterizer->setupPlane(m_planesToDraw[pl]);
      m_rasterizer->prepareLoop();
      m_rasterizer->loopPixels(&p);
    }       

    update();
}



void CgPanel::prepareScene()
{
    xaverage = 0; // for partioning
    yaverage = 0; // for partioning
    zaverage = 0; // for partioning
    
    scene::Scene * theScene = m_sceneMgr->getScene();
    m_nrOfTransformations = 0;
    
    // collect shapes to draw
    for(int i = 0; i < theScene->getSize(); ++i){
      //start new transformationmatrix
      m_currentTransform = new FloatMatrix();
      m_currentTransform->setIdentity();
      
      //start recursion
      walkThroughScene(theScene->getSubScene(i));
    }
  
    cout << "Number of vertices:" << m_planesToDraw.size() << endl;
}

void CgPanel::walkThroughScene(scene::SubScene * temp)
{
    if(temp->isRecursive()){
     
      draw::FloatMatrix * old = m_currentTransform;
     
      m_currentTransform =
          m_currentTransform->multiplyWith(temp->getTransformation());
      
      m_nrOfTransformations++;
      //cout << "\nTransformations= " << m_nrOfTransformations << endl;
      //temp->getTransformation()->print();
      //cin.get();
      
      for(int i = 0; i < temp->getSize();++i)
        walkThroughScene(temp->getSubScene(i));
    
      m_currentTransform = old;
      
      m_nrOfTransformations--;
    }
    else{
      if(m_nrOfTransformations != 0)
       
((scene::Shape*)temp)->setTransformation(m_currentTransform,m_sceneMgr->
getObjectList());
       
      int oid = ((scene::Shape*)temp)->getGeo()->getObjectId();
      object::Object * obj = m_sceneMgr->getObjectList()->getObject(oid);
      scene::Material * mat = ((scene::Shape*)temp)->getMat();
      scene::Texture * tex = ((scene::Shape*)temp)->getTex();
      
      for(int i = 0; i < obj->nrOfPlanes(); ++i){
        obj->getPlane(i)->setMat(mat);
        obj->getPlane(i)->setTex(tex);
        obj->getPlane(i)->setBoundingBox();
        
        
        m_planesToDraw.push_back(obj->getPlane(i));
    
        
        
      }
      
    }
      
}

CgPanel::~CgPanel()
{
    cout << "Deleting raytracer...\n";
    if(m_rayTracer != NULL)
      delete m_rayTracer;
    
    cout << "Deleting rasterizer...\n";
    if(m_rasterizer != NULL)
      delete m_rasterizer;
    
    cout << "Deleting scene manager...\n";
    if(m_sceneMgr!= NULL)
      delete m_sceneMgr;
    

}

void CgPanel::saveImage()
{
    // save file
    QImage image = m_pixMap.toImage();
    QByteArray ba;
    
    QString fileName = QFileDialog::getSaveFileName(this,
      tr("Save where..."),
"/home/maarten/Documents/CG/project2/assets/rendered_scenes", 
              tr("Png files(*.png)")); 
    QFile newFile(fileName + ".png" );
    newFile.open(QIODevice::WriteOnly);
    image.save(&newFile, "PNG"); // writes image into ba in PNG format
}
void CgPanel::drawPixel(int x, int y, QColor color)
{
    QPainter p( this );
    p.setPen(color);
    p.drawPoint(x, y);
    repaint();
    
}

void CgPanel::setBgColor ( float r, float g, float b )
{
    m_bgColor.setRgbF(r,g,b);
    m_rayTracer->setBgcolor(r,g,b);
    m_pixMap.fill(m_bgColor);
    update();
}

} // end namespace draw
